package com.clei.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer;

/**
 * cmd util
 *
 * @author Y
 * @date 2024-03-05
 **/
public class CmdUtil {

    /**
     * cmdArr
     */
    private final static String[] CMD_ARR = new String[2];

    /**
     * 输出内容charset
     */
    private final static Charset OUTPUT_CHARSET;

    /**
     * 默认输出处理器
     */
    private final static Consumer<String> DEFAULT_OUTPUT_HANDLER = PrintUtil::println;

    // ENV、CMD_ARR、OUTPUT_CHARSET初始化
    static {
        boolean windows = SystemUtil.isWindows();
        if (windows) {
            CMD_ARR[0] = "cmd";
            CMD_ARR[1] = "/C";
            OUTPUT_CHARSET = Charset.forName("GBK");
        } else {
            // 默认非windows即linux，且bash可用
            CMD_ARR[0] = "bash";
            CMD_ARR[1] = "-c";
            OUTPUT_CHARSET = StandardCharsets.UTF_8;
        }
    }

    /**
     * exec
     *
     * @param cmd 命令和参数
     * @return exp 是否发生异常 true/false
     */
    public static boolean exec(String cmd) {
        return exec(cmd, DEFAULT_OUTPUT_HANDLER);
    }

    /**
     * exec
     *
     * @param cmd              命令和参数
     * @param workingDirectory 工作目录
     * @return exp 是否发生异常 true/false
     */
    public static boolean exec(String cmd, String workingDirectory) {
        return exec(cmd, workingDirectory, DEFAULT_OUTPUT_HANDLER);
    }

    /**
     * exec
     *
     * @param cmd                 命令和参数
     * @param normalOutputHandler 正常输出内容处理器
     * @return exp 是否发生异常 true/false
     */
    public static boolean exec(String cmd, Consumer<String> normalOutputHandler) {
        return exec(cmd, normalOutputHandler, DEFAULT_OUTPUT_HANDLER);
    }

    /**
     * exec
     *
     * @param cmd                 命令和参数
     * @param workingDirectory    工作目录
     * @param normalOutputHandler 正常输出内容处理器
     * @return exp 是否发生异常 true/false
     */
    public static boolean exec(String cmd, String workingDirectory, Consumer<String> normalOutputHandler) {
        return exec(cmd, workingDirectory, normalOutputHandler, DEFAULT_OUTPUT_HANDLER);
    }

    /**
     * exec
     *
     * @param cmd 命令和参数
     * @return exp 是否发生异常 true/false
     */
    public static boolean exec(String cmd, Consumer<String> normalOutputHandler, Consumer<String> errorOutputHandler) {
        return exec(cmd, null, normalOutputHandler, errorOutputHandler);
    }

    /**
     * exec
     *
     * @param cmd                 命令和参数
     * @param workingDirectory    工作目录
     * @param normalOutputHandler 正常输出内容处理器
     * @param errorOutputHandler  异常输出内容处理器
     * @return exp 是否发生异常 true/false
     */
    public static boolean exec(String cmd, String workingDirectory, Consumer<String> normalOutputHandler, Consumer<String> errorOutputHandler) {
        boolean exp = false;
        String[] cmdArr = getCmdArr(cmd);
        try {
            Runtime runtime = Runtime.getRuntime();
            Process exec = null == workingDirectory ? runtime.exec(cmdArr) : runtime.exec(cmdArr, null, new File(workingDirectory));
            printExec(exec.getInputStream(), normalOutputHandler);
            printExec(exec.getErrorStream(), errorOutputHandler);
            exec.waitFor();
            exec.destroy();
        } catch (Exception e) {
            PrintUtil.log("exec error cmd : {}", cmd, e);
            exp = true;
        }
        return exp;
    }

    /**
     * getCmdArr
     *
     * @param cmd 命令和参数
     * @return cmdArr
     */
    private static String[] getCmdArr(String cmd) {
        return new String[]{CMD_ARR[0], CMD_ARR[1], cmd};
    }

    /**
     * print
     *
     * @param is          InputStream
     * @param lineHandler lineHandler
     */
    private static void printExec(InputStream is, Consumer<String> lineHandler) {
        try (InputStreamReader isr = new InputStreamReader(is, OUTPUT_CHARSET);
             BufferedReader br = new BufferedReader(isr)) {
            String line;
            while (null != (line = br.readLine())) {
                lineHandler.accept(line);
            }
        } catch (Exception e) {
            PrintUtil.log("printExec error ", e);
        }
        PrintUtil.separatorLine();
    }
}
